// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/builtins/builtins-utils.h"
#include "src/builtins/builtins.h"
#include "src/counters.h"
#include "src/objects-inl.h"

namespace v8 {
namespace internal {

    // -----------------------------------------------------------------------------
    // ES6 section 20.2.2 Function Properties of the Math Object

    // ES6 section 20.2.2.18 Math.hypot ( value1, value2, ...values )
    BUILTIN(MathHypot)
    {
        HandleScope scope(isolate);
        int const length = args.length() - 1;
        if (length == 0)
            return Smi::kZero;
        DCHECK_LT(0, length);
        double max = 0;
        bool one_arg_is_nan = false;
        std::vector<double> abs_values;
        abs_values.reserve(length);
        for (int i = 0; i < length; i++) {
            Handle<Object> x = args.at(i + 1);
            ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x,
                Object::ToNumber(isolate, x));
            double abs_value = std::abs(x->Number());

            if (/*std::*/isnan(abs_value)) {
                one_arg_is_nan = true;
            } else {
                abs_values.push_back(abs_value);
                if (max < abs_value) {
                    max = abs_value;
                }
            }
        }

        if (max == V8_INFINITY) {
            return *isolate->factory()->NewNumber(V8_INFINITY);
        }

        if (one_arg_is_nan) {
            return ReadOnlyRoots(isolate).nan_value();
        }

        if (max == 0) {
            return Smi::kZero;
        }
        DCHECK_GT(max, 0);

        // Kahan summation to avoid rounding errors.
        // Normalize the numbers to the largest one to avoid overflow.
        double sum = 0;
        double compensation = 0;
        for (int i = 0; i < length; i++) {
            double n = abs_values[i] / max;
            double summand = n * n - compensation;
            double preliminary = sum + summand;
            compensation = (preliminary - sum) - summand;
            sum = preliminary;
        }

        return *isolate->factory()->NewNumber(std::sqrt(sum) * max);
    }

} // namespace internal
} // namespace v8
